package com.twl.app.base.util;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;

import com.twl.app.base.bo.CryptService;
import com.twl.app.base.entity.Bfsalary;
import com.twl.app.base.entity.Dispatchmanger;
import com.twl.app.base.entity.Dutyback;
import com.twl.app.base.entity.Dutycount;
import com.twl.app.base.entity.Dutydeduct;
import com.twl.app.base.entity.Dutymeals;
import com.twl.app.base.entity.Dutymoney;
import com.twl.app.base.entity.Dutysendmoney;
import com.twl.app.base.entity.Jbsalary;
import com.twl.app.base.entity.Jsbfmoney;
import com.twl.app.base.entity.Leavebmoney;
import com.twl.app.base.entity.MinMoney;
import com.twl.app.base.entity.Moneyadjust;
import com.twl.app.base.entity.Overtimemeals;
import com.twl.app.base.entity.Overtimemoney;
import com.twl.app.base.entity.Personalduty;
import com.twl.app.base.entity.Personalmoney;
import com.twl.app.base.entity.SalSalaryData;
import com.twl.app.base.entity.Secureunit;
import com.twl.app.base.entity.Shiftallowance;
import com.twl.app.base.entity.Solidmoney;
import com.twl.app.base.entity.Tradecost;
import com.twl.app.base.entity.Uemp;
import com.twl.common.constant.SalConstant;

/**
 * 薪资系统加密解密工具
 * 
 * @author Val
 * 
 */
public class CryptUtils {
	/**
	 * 需要加密解密的Model类
	 */
	private final static Class<?>[] CRYPT_CLASSES = { Bfsalary.class, Dispatchmanger.class, Dutyback.class, Dutycount.class, Dutydeduct.class, Dutymeals.class, Dutymoney.class, Dutysendmoney.class, Jbsalary.class, Jsbfmoney.class, Leavebmoney.class, MinMoney.class, Moneyadjust.class, Overtimemeals.class, Overtimemoney.class, Personalduty.class, Personalmoney.class, Secureunit.class, Tradecost.class,Uemp.class,SalSalaryData.class ,Shiftallowance.class,Solidmoney.class};

	/**
	 * 需要加密解密的变量名称
	 */
	private final static String[] CRYPT_FIELDS = { "bfmoney", "moneyadjust", "overtimemoney", "overtimemeals", "shiftallowance", "shiftallowances", "loyalaward", "dutycount",  "mainAll", "otherAll", "hightEmp", "duyWrad", "incenWrad", "recommWrad", "autoWrad", "cusWrad", "cusicWrad", "custYwrad", "cusapFwrad", "custhWrad", "collWrad", "collthWrad", "directComm", "direthComm", "direperWrad", "direamWrad", "direanWrad", "direthWrad", "hfisubWrad", "hfishWrad", "hfiperWrad", "hfiminWrad", "hfisthWrad", "branchWrad", "branptWrad", "brancaWrad", "branhgWrad", "branthWrad", "branqkWrad", "branfwWrad", "brandtlWrad", "brandmaxWrad", "branswiWrad", "branofWrad", "brancommWrad", "brantranWrad", "bradpayWrad", "bransupmhWrad", "brandepWrad", "branperWrad", "branatoWrad", "branoerWrad",
			"branextWrad", "branriskWrad", "nobrandWrad", "brandbitWrad", "otherWrad", "emiccoPtion", "dutysendmoney", "overtimemoney", "overtimemeals", "shiftallowance", "shiftallowances", "personalmoney", "perreporduty", "benchmark", "tradecost", "mincount", "leavebmoney", "amountdou", "dutytpmoney", "staffmonery", "dutymoney", "dutydeduct", "staffmoney", "amountsick", "amountthing", "amountmeals", "moneycount", "perdermoney", "tradecostdou","monthlyaward","halfYearAwrad","yearawrad","duyWrad","incenWrad","mainAll","otherAll","maternityPay","recommWrad","highTemp","dutyadds","afterbonus","parentsonly","emicCoption","ataxAdsubit","dutydeduction","autoWrad","cusWrad","cusicWrad","custyWrad","cusapfWrad","custhWrad","collWrad","collthWrad","directComm","direthComm","direperWrad","direamWrad","direanWrad","direthWrad","hfisubWrad","hfishWrad","hfiperWrad","hfiminWrad","hfisthWrad","branchWrad","branptWrad","brancaWrad","branhgWrad","branthWrad","branqkWrad","branfwWrad","brandtlWrad","brandmaxWrad","branswIwrad","branofWrad","brancommWrad","brantranWrad","bradpayWrad","bransupmhWrad","brandepWrad","branperWrad","branatoWrad","branoerWrad","branextWrad","branriskWrad","nobrandWrad","brandbitWrad","otherWrad","ruletPats","dutymeals","annualWageConvert" };
	/*
	 * ======= private final static String[] CRYPT_FIELDS = { "bfmoney",
	 * "moneyadjust", "overtimemoney", "overtimemeals", "shiftallowance",
	 * "shiftallowances", "branchmoney", "performamcem", "loyalaward",
	 * "yearaward", "dutyadd", "dutycount", "datemoney", "mainAll", "otherAll",
	 * "hightEmp", "duyWrad", "incenWrad", "recommWrad", "autoWrad", "cusWrad",
	 * "cusicWrad", "custYwrad", "cusapFwrad", "custhWrad", "collWrad",
	 * "collthWrad", "directComm", "direthComm", "direperWrad", "direamWrad",
	 * "direanWrad", "direthWrad", "hfisubWrad", "hfishWrad", "hfiperWrad",
	 * "hfiminWrad", "hfisthWrad", "branchWrad", "branptWrad", "brancaWrad",
	 * "branhgWrad", "branthWrad", "branqkWrad", "branfwWrad", "brandtlWrad",
	 * "brandmaxWrad", "branswiWrad", "branofWrad", "brancommWrad",
	 * "brantranWrad", "bradpayWrad", "bransupmhWrad", "brandepWrad",
	 * "branperWrad", "branatoWrad", "branoerWrad", "branextWrad",
	 * "branriskWrad", "nobrandWrad", "brandbitWrad", "otherWrad",
	 * "emiccoPtion", "dutysendmoney", "overtimemoney", "overtimemeals",
	 * "shiftallowance", "shiftallowances", "personalmoney", "perreporduty",
	 * "benchmark", "tradecost", "mincount", "leavebmoney", "amountdou",
	 * "dutytpmoney", "staffmonery", "dutymoney", "dutydeduct", "staffmoney",
	 * "amountsick", "amountthing", "amountmeals", "moneycount", "perdermoney",
	 * "tradecostdou" }; >>>>>>> .r940
	 */

	private final static String DES_KEY = "desKey";

	/**
	 * 加密 T
	 * 
	 * @param t
	 * @return
	 * @throws Exception
	 */
	public static <T> T encrypt(T t) throws Exception {
		Class<?> c = t.getClass();
		t = encryptDES(c, t);
		return t;
	}
	
	/**
	 * 加密 T 加密后的是一个新的对象
	 * 
	 * @param t
	 * @return
	 * @throws Exception
	 */
	public static <T> T encryptNew(T t) throws Exception {
		@SuppressWarnings("unchecked")
		T t_ = (T) t.getClass().newInstance();
		BeanUtils.copyProperties(t_, t);
		Class<?> c = t.getClass();
		t_ = encryptDES(c, t_);
		return t_;
	}
	
	

	/**
	 * 加密List<T>
	 * 
	 * @param list
	 * @return
	 * @throws Exception
	 */
	public static <T> List<T> encrypt(List<T> list) throws Exception {
		for (T t : list) {
			try{
				t = encrypt(t);
			}catch(Exception e){
				e.printStackTrace();
			}
		}
		return list;
	}

	/**
	 * 加密List<T> 加密后的是一个新的List
	 * 
	 * @param list
	 * @return
	 * @throws Exception
	 */
	public static <T> List<T> encryptNew(List<T> list) throws Exception {
		List<T> list_ = new ArrayList<T>();
		for (T t : list) {
			try {
				t = encryptNew(t);
			} catch (Exception e) {
				e.printStackTrace();
			}
			list_.add(t);
		}
		return list_;
	}
	
	
	/**
	 * 解密 T
	 * 
	 * @param t
	 * @return
	 * @throws Exception
	 */
	public static <T> T decrypt(T t) throws Exception {
		Class<?> c = t.getClass();
		t = decryptDES(c, t);
		return t;
	}

	/**
	 * 解密 T解密后的是一个新的对象
	 * 
	 * @param t
	 * @return
	 * @throws Exception
	 */
	public static <T> T decryptNew(T t) throws Exception {
		@SuppressWarnings("unchecked")
		T t_ = (T) t.getClass().newInstance();
		BeanUtils.copyProperties(t_, t);
		Class<?> c = t.getClass();
		t_ = decryptDES(c, t_);
		return t_;
	}
	
	
	
	
	/**
	 * 解密 List<T>
	 * 
	 * @param list
	 * @return
	 * @throws Exception
	 */
	public static <T> List<T> decrypt(List<T> list) throws Exception {
		for (T t : list) {
			try{
				t = decrypt(t);
			}catch(Exception e){
				e.printStackTrace();
			}
		}
		return list;
	}
	
	/**
	 * 解密 List<T> 解密后的是一个新的List
	 * 
	 * @param list
	 * @return
	 * @throws Exception
	 */
	public static <T> List<T> decryptNew(List<T> list) throws Exception {
		List<T> list_ = new ArrayList<T>();
		for (T t : list) {
			try{
				t = decryptNew(t);
			}catch(Exception e){
				e.printStackTrace();
			}
			list_.add(t);
		}
		return list_;
	}
	
	

	/**
	 * 调用3DES算法进行加密 (反射)
	 * 
	 * @param c
	 * @param t
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws Exception
	 */
	@Deprecated
	private static <T> T encryptDES2(Class<?> c, T t) throws IllegalArgumentException, IllegalAccessException, Exception {
		// 判断Model是否需要加解密 ,如果密钥Map为空则不进行加密
		if (Arrays.asList(CRYPT_CLASSES).contains(t.getClass())) {

			if (!c.getSuperclass().isAssignableFrom(Object.class)) {
				Class<?> sc = c.getSuperclass();
				t = encryptDES(sc, t);
			}

			// 最新密钥主键密文
			String lastKeyId = CryptService.getValueByKey(SalConstant.LAST_KEY_ID);

			// 最新密钥
			String lastKey = CryptService.getValueByKey(SalConstant.LAST_KEY);

			if (StringUtils.isNotBlank(lastKey)) {

				Field[] fields = c.getDeclaredFields();

				for (Field f : fields) {
					f.setAccessible(true);
					// 判断该变量是否需要加解密
					if (Arrays.asList(CRYPT_FIELDS).contains(f.getName())) {
						if (f.getType().isAssignableFrom(String.class) && f.get(t) != null) {
							// 获取密文
							String cipher = ThreeDESUtils.encryptThreeDESECB(f.get(t).toString(), lastKey);
							//加密
							f.set(t, cipher);
						}
					}
					// 将密钥主键存放到desKey字段中
					if (DES_KEY.equals(f.getName())) {
						f.set(t, lastKeyId);
					}

				}

			}

		}
		return t;
	}
	
	
	/**
	 * 调用3DES算法进行加密 (内省)
	 * 
	 * @param c
	 * @param t
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws Exception
	 */
	private static <T> T encryptDES(Class<?> c, T t) throws IllegalArgumentException, IllegalAccessException, Exception {
		// 判断Model是否需要加解密 ,如果密钥Map为空则不进行加密
		if (Arrays.asList(CRYPT_CLASSES).contains(t.getClass())) {

			// 最新密钥主键密文
			String lastKeyId = CryptService.getValueByKey(SalConstant.LAST_KEY_ID);

			// 最新密钥
			String lastKey = CryptService.getValueByKey(SalConstant.LAST_KEY);

			if (StringUtils.isNotBlank(lastKey)) {
				
				BeanInfo bi = Introspector.getBeanInfo(c);
				PropertyDescriptor [] pds = bi.getPropertyDescriptors();
				for (PropertyDescriptor p : pds) {
					// 排除class
					if (!Class.class.equals(p.getPropertyType())) {
						// 判断该变量是否需要加解密
						if (Arrays.asList(CRYPT_FIELDS).contains(p.getName())) {
							Object obj = p.getReadMethod().invoke(t);
							if ((obj instanceof String) && obj != null) {
								// 获取密文
								String cipher = ThreeDESUtils.encryptThreeDESECB(obj.toString(), lastKey);
								// 加密
								p.getWriteMethod().invoke(t, cipher);
							}
						}
						// 将密钥主键存放到desKey字段中
						if (DES_KEY.equals(p.getName())) {
							p.getWriteMethod().invoke(t, lastKeyId);
						}
					}
				}
			}
		}
		return t;
	}
	
	
	

	/**
	 * 调用3DES算法进行解密(反射)
	 * 
	 * @param c
	 * @param t
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws Exception
	 */
	@Deprecated
	private static <T> T decryptDES2(Class<?> c, T t) throws IllegalArgumentException, IllegalAccessException, Exception {
		// 判断Model是否需要加解密,如果密钥Map为空则不进行加密
		if (Arrays.asList(CRYPT_CLASSES).contains(t.getClass())) {
			if (!c.getSuperclass().isAssignableFrom(Object.class)) {
				Class<?> sc = c.getSuperclass();
				t = decryptDES(sc, t);
			}

			Field desKey = null;
			try {
				desKey = c.getDeclaredField(DES_KEY);
			} catch (Exception e) {
			}
			if (desKey != null) {

				desKey.setAccessible(true);
				String keyId = desKey.get(t) == null ? null : desKey.get(t).toString();
				// 如果该条记录未加密则不解密
				if (StringUtils.isNotBlank(keyId)) {
					// 得到加密密钥
					String key = CryptService.getValueByKey(keyId);
					if (StringUtils.isNotBlank(key)) {
						Field[] fields = c.getDeclaredFields();
						for (Field f : fields) {
							f.setAccessible(true);
							// 判断该变量是否需要加解密
							if (Arrays.asList(CRYPT_FIELDS).contains(f.getName())) {
								if (f.getType().isAssignableFrom(String.class) && f.get(t) != null) {
									// 获取明文
									String plaintext = ThreeDESUtils.decryptThreeDESECB(f.get(t).toString(), key);
									f.set(t, plaintext);
								}
							}
						}
					}
				}
			}
		}
		return t;
	}
	
	/**
	 * 调用3DES算法进行解密(内省)
	 * 
	 * @param c
	 * @param t
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws Exception
	 */
	private static <T> T decryptDES(Class<?> c, T t) throws IllegalArgumentException, IllegalAccessException, Exception {
		// 判断Model是否需要加解密,如果密钥Map为空则不进行加密
		if (Arrays.asList(CRYPT_CLASSES).contains(t.getClass())) {
			PropertyDescriptor desKey = new PropertyDescriptor(DES_KEY, c);
			if (desKey != null) {
				String keyId = desKey.getReadMethod().invoke(t) == null ? null : desKey.getReadMethod().invoke(t).toString();
				// 如果该条记录未加密则不解密
				if (StringUtils.isNotBlank(keyId)) {
					// 得到加密密钥
					String key = CryptService.getValueByKey(keyId);
					if (StringUtils.isNotBlank(key)) {
						BeanInfo bi = Introspector.getBeanInfo(c);
						PropertyDescriptor [] pds = bi.getPropertyDescriptors();
						for (PropertyDescriptor p : pds) {
							// 排除class
							if (!Class.class.equals(p.getPropertyType())) {
								// 判断该变量是否需要加解密
								if (Arrays.asList(CRYPT_FIELDS).contains(p.getName())) {
									Object obj = p.getReadMethod().invoke(t);
									if ((obj instanceof String) && obj != null) {
										// 获取明文
										String plaintext = ThreeDESUtils.decryptThreeDESECB(obj.toString(), key);
										//解密
										p.getWriteMethod().invoke(t, plaintext);
									}
								}
							}
						}
					}
				}
			}
		}
		return t;
	}

}
